﻿using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Net.Http.Headers;

namespace DotNetHackathon.GBA.ApiCommon.ExceptionHandling
{
    public class ExceptionHandlingMiddleware
    {
        private readonly RequestDelegate _next;
        private readonly Func<object, Task> _clearCacheHeadersDelegate;

        public ExceptionHandlingMiddleware(RequestDelegate next)
        {
            _next = next;
            _clearCacheHeadersDelegate = ClearCacheHeaders;
        }

        public async Task InvokeAsync(HttpContext context)
        {
            try
            {
                await _next(context);
            }
            catch (Exception ex)
            {
                if (context.Response.HasStarted)
                {
                    var logger = context.RequestServices.GetRequiredService<ILogger<ExceptionHandlingMiddleware>>();
                    logger.LogWarning("异常已发生，但响应已经开始!");
                }

                await HandleAndWrapException(context, ex);
            }
        }

        private async Task HandleAndWrapException(HttpContext httpContext, Exception exception)
        {
            var statusCodeFinder = httpContext.RequestServices.GetRequiredService<IHttpExceptionStatusCodeFinder>();
            var responseTextConverter = httpContext.RequestServices.GetRequiredService<IExceptionToResponseTextConverter>();

            httpContext.Response.StatusCode = (int)statusCodeFinder.GetStatusCode(httpContext, exception);
            httpContext.Response.OnStarting(_clearCacheHeadersDelegate, httpContext.Response);
            await httpContext.Response.WriteAsync(
                responseTextConverter.Convert(exception)
                );
        }

        private Task ClearCacheHeaders(object state)
        {
            var response = (HttpResponse)state;
            response.Headers[HeaderNames.ContentType] = "application/json;charset=utf-8";
            response.Headers[HeaderNames.CacheControl] = "no-cache";
            response.Headers[HeaderNames.Pragma] = "no-cache";
            response.Headers[HeaderNames.Expires] = "-1";
            response.Headers.Remove(HeaderNames.ETag);

            return Task.CompletedTask;
        }
    }
}
